#pragma once

#include <string>
#include <stdexcept>

#include <GL\glew.h>
#include <GL\freeglut.h>
#include <glm\glm.hpp>

#include "Sombras.h"
#include "ShaderLoader.h"
#include "TextureLoader.h"


// Para ser utilizada por una primitiva (ejem: cubo)
class Renderizable {
public:
	Renderizable();
	virtual ~Renderizable();

	// Inicializa el elemento y setea la posicion de la luz direccional
	virtual void init(const glm::vec3 &DireccionLuz, std::string vertShaderFileName, std::string fragShaderFileName);

	// Permite setear los coeficientes usados en la iluminacion del elemento
	virtual void setMaterial(const glm::vec3 &coefDifusion, const glm::vec3 &coefAmbiente, const glm::vec3 &coefEspecular, float shininess);

	// Setear la textura del elemento, se hace una sola vez luego de haber creado el glprogramHandler
	virtual void setTextura(const std::string filename);

	// Setear la textura que se usa como mapa de normales, similar a setTextura pero en la unidad 1 de textura
	virtual void setNormalMap(const std::string filename);

	// Setear las texturas del cube map, se hace una sola vez luego de haber creado el glprogramHandler
	virtual void setCubeMap();

	// Dibuja en pantalla el elemento usando las matrices de transformacion pasadas
	virtual void renderizar(const glm::mat4 &matVista, const glm::mat4 &matProyeccion, const glm::mat4 &matModelo) const = 0;

protected:
	// Carga el shader program y devuelve su ID por si alguien lo quiere usar
	virtual GLuint cargarShaderProgram() const;

	// Carga las matrices de transformacion en el shader, el "shaderProgramID" debe estar cargado 
	virtual void cargarMatricesTransformacion(const glm::mat4 &viewMat, const glm::mat4 &projectionMat, const glm::mat4 &modelMat) const;

	// Carga los datos necesarias para la iluminacion del elemento, el "shaderProgramID" debe estar cargado 
	virtual void cargarDatosIluminacion(const glm::mat4 &matModel, const glm::mat4 &matView, float alpha = 1.0f) const;

	// Carga la textura asociada al elemento con el metodo setTextura, el "shaderProgramID" debe estar cargado 
	// Se hace el Bind a Text1 (variable usada en el shader)
	virtual void cargarTextura() const;

	// Carga la textura de mapa de normales
	// Se hace el Bind a NormalMap (variable usada en el shader)
	virtual void cargarNormalMap() const;

	// Carga las texturas del cube map
	// Se hace el Bind a CubeMap (variable usada en el shader)
	virtual void cargarCubeMap() const;

	virtual bool isTexturable() const;

private:
	// Los datos de la iluminacion
	glm::vec3 m_coefDifuso;
	glm::vec3 m_coefAmbiente;
	glm::vec3 m_coefEspecular;
	float m_shininess;

	glm::vec3 m_dirLuzDireccional;
	glm::mat4 m_dephTrans;

	// El encargado de manejar los shaders
	ShaderLoader m_shaders;

	// El encargado de manejar la textura
	TextureLoader m_textura;

	Sombras m_shadows;
};

